package mop.domain.project;

import java.util.LinkedList;
import java.util.List;

import mop.domain.task.Task;
import mop.domain.task.TaskPool;
import mop.exception.IllegalAddTaskException;
import mop.exception.IllegalDescriptionException;
import mop.exception.IllegalDuplicationException;
import mop.exception.IllegalProjectException;
import mop.exception.IllegalTaskException;

/**
 * Class for ProjectPool
 * This class is a singleton!
 * 
 * @invar	No two projects have the same description.
 * 			| foreach(description) 
 *  		| isUniqueProject(description)
 */
public class ProjectPool 
{

	/**
	 * Creates a ProjectPool with no extra settings.
	 * Constructor must be at protected level.
	 * Allows inheritance, but no out of package creation.
	 * 
	 * @post	Projects is set to a new LinkedList.
	 */
	protected ProjectPool()
	{
		this.projects = new LinkedList<Project>();
	}
	
	
	/***************************************************************************
	 * 							instance
	 ***************************************************************************/
	
	/**
	 * Always use this function to access ProjectPool.
	 * 
	 * @return 	The static instance of ProjectPool.
	 */
	public static ProjectPool getInstance()
	{
		if(instance == null) 
		{
	         instance = new ProjectPool();
	    }
		return instance;	
	}
	
	/**
	 * Instance to self.
	 */
	private static ProjectPool instance = null;
	
	
	/***************************************************************************
	 * 							Projects container
	 ***************************************************************************/
	
	/**
	 * Returns a list of all the projects in the pool.
	 * 
	 * @return 	A list of projects by string (descriptions).
	 */
	public List<String> getProjectList()
	{
		List<String> result = new LinkedList<String>();
		
		for(Project p : projects)
		{
			result.add(p.getDescription());
		}
		
		return result;
	}
	
	/**
	 * Returns a project object, based on a given project description.
	 * Useful for methods in ProjectPool, but not meant to be used outside ProjectPool.
	 * 
	 * @param 	description
	 * 			A project description.
	 * 
	 * @pre		description must be initialized and not empty.
	 * 
	 * @return	A Project object if the project exists, otherwise null.
	 */
	private Project getProject(String description)
	{
		for(Project p : projects)
		{
			if(p.getDescription().equals(description))
			{
				return p;
			}
		}
		
		return null;
	}
	
	/**
	 * Removes a project from the list.
	 * 
	 * @param 	description
	 *  		The description of the project you want to remove from the pool.
	 * 
	 * @pre 	String exists in the project database. If it doesn't, we ignore the remove.
	 * 
	 * @effect 	Terminates the project.
	 * 
	 * @post 	The project with his description = $description is removed from the list.
	 */
	public void removeProject(String description)
	{
		Project p = getProject(description);
		if(p != null)
		{
			p.terminate();
			projects.remove(p);
		}
	}
	
	 /**
	  * Checks if the given description is not already associated with a project in the container.
	  * 
	  * @param 	description
	  *  		The description you want checked.
	  * 
	  * @return	true if a project with the given description cannot be found in the database.
	  */
	 public boolean isUniqueProject(String description)
	 {
		 return getProject(description) == null;
	 }
	
	/**
	 * Adds a project to the container.
	 * 
	 * @param 	description
	 * 			The description of the project you want to create.
	 * 
	 * @post 	A new project instance is created with the given description.
	 * 
	 * @throws 	IllegalDuplicateException
	 * 			if the project is a duplicate
	 * 			|!isUniqueProject(description)
	 *       
	 * @throws 	IllegalDescriptionException
	 * 			if the description isn't a non-empty string
	 * 			|!isValidDescription(description)
	 */
	public void addProject(String description) 
		throws IllegalDescriptionException, IllegalDuplicationException
	{
		if(isUniqueProject(description))
		{
			Project p = new Project(description);
			this.projects.add(p);
		}
		else
		{
			throw new IllegalDuplicationException(description);
		}
	}
	 
	
	/**
	 * list to store the projects in 
	 */
	private List<Project> projects;
	
	
	/***************************************************************************
	 * 							Tasks
	 ***************************************************************************/
	
	/**
	 * Adds a task to a project.
	 * 
	 * @param 	task
	 *  		The description of the task.
	 *  
	 * @param 	project
	 *  		The project to add the task to.
	 *  
	 * @post 	task is added to list.
	 * 
	 * @throws 	IllegalTaskException
	 *  		if the task can not be found
	 *  		| isUniqueTask(task)
	 * 
	 * @throws 	IllegalAddTaskException
	 *  		if the task already has a project
	 *  		| task.getproject != null
	 *  
	 * @throws 	IllegalProjectException
	 *  		if the project can not be found
	 */
	public void addTask(String project, String task) 
		throws IllegalTaskException,IllegalAddTaskException,IllegalProjectException
	{
		Task t = TaskPool.getInstance().getTask(task);
		
		Project p = getProject(project);
		
		if(p != null)
		{
			p.addTask(t);
		}
		else
		{
			throw new IllegalProjectException(project);
		}
	}
	
	/**
	 * Returns the task list of a certain project.
	 * 
	 * @param 	project
	 *  		The project (description) that needs to return a task list.
	 * 
	 * @return 	A list of strings with task descriptions.
	 * 
	 * @throws 	IllegalProjectException
	 *  		if the project can not be found
	 */
	public List<String> getTasks(String project) 
		throws IllegalProjectException
	{
		Project p = getProject(project);
		if(p != null)
		{
			return p.getTasks();
		}
		else
		{
			throw new IllegalProjectException(project);
		}
	}
	
}